#ifndef _ML_TRAOD_GRID_H_
#define _ML_TRAOD_GRID_H_
#include "MLCommon.h"
#include "MLGlobals.h"
#include "MLDataPoint.h"
#include "MLDataVectorAdder.h"
#include "MLTrajectory.h"
#include "MLDistanceFunctions.h"

class CMLTRAODGrid
{
public:
	// Constructor and Destructor
	CMLTRAODGrid(void);
	~CMLTRAODGrid(void);

private:
	// Inner data types
	struct TangentPoint_
	{
		int traj;
		int part;
		int index;
		mylablib::CMLDataPoint loc; // location
		mylablib::CMLDataPoint dir; // normalized direction vector
		int numcloseto; // number of points close to itself
	};
	
	struct Grid_
	{
		Grid_()
		{
			bAccessMark = false;
		}

		bool bAccessMark; // mark it is accessed already
		std::vector<TangentPoint_> vTPs; // the set of Tangent points
	};

public:
	// Methods
	int Initialize(CMLTrajectoryCollection* pTrajs);
	int DetectOutlier(std::vector<int>& vOutlierIndexes);

public:
	// Properties
	inline void SetPointDimension(int d)   { m_nPointDimension = d; };
	inline void SetOutlyingDist(double dD) { m_dOutlyingDist = dD; };
	inline void SetOutlyingFrac(double dF) { m_dOutlyingFrac = dF; };
	inline void SetOutlyingPortion(double dP) { m_dOutlyingPortion = dP; };
	inline void SetGridLocationSize(double dSize) { m_dGridLocationSize = dSize; };
	inline void SetGridDirectionSize(double dSize) { m_dGridDirectionSize = dSize; };
	inline void SetTangentPointSampleSize(double dSize) { m_dTangentSampleSize = dSize; };
	inline void SetDistWeights(double dLocWeight, double dDirWeight) 
	{ 
		m_dLocationDistWeight = dLocWeight; 
		m_dDirectionDistWeight = dDirWeight; 
	}
	inline long GetMaxMemoryUsage() const { return m_nMaxMemoryUsage; };
	inline int GetGridSize() const { return (int)m_vGrids.size(); };



private:
	// Implementations
	void Clear();
	int FindLocationBoundary(void);
	int CreateGrids(void);
	int FillGrids(void);
	int FillTrajectoryToGrids(int nTrajIndex, const CMLTrajectory& traj);
	int FillPartitionToGrids(int nTrajIndex, int nPartIndex, int& nTPIndex, const mylablib::CMLDataPoint& p1, const mylablib::CMLDataPoint& p2);
	int FindTrajOutliers(std::vector<int>& vOutlierIndexes);
	int CheckTangentPointsOfGrids(void);
	int CalculateGridSearchRanges(void);
	int CheckTangentPointOutlying(int nGridIndex, TangentPoint_& tp);

private:
	// Tools
	inline int GetPointDimension(void) const { return m_nPointDimension; };

	inline double SquaredDist(const TangentPoint_& tp1, const TangentPoint_& tp2) 
	{
		double loc_dist = mylablib::CMLEuclideanDistance::Dist(tp1.loc, tp2.loc) * m_dLocationDistWeight;
		double dir_dist = mylablib::CMLEuclideanDistance::Dist(tp1.dir, tp2.dir) * m_dDirectionDistWeight;
		return loc_dist*loc_dist + dir_dist*dir_dist;
	}

	inline void GetGridCoordIndexes(mylablib::CMLDataVector<int>& indexes, const TangentPoint_& tp) const
	{
		int d;
		for (d=0; d<GetPointDimension(); d++)
		{
			indexes[d] = (int)((tp.loc[d]-m_MinLocationBoundary[d]) / m_dGridLocationSize);
			if (indexes[d] >= m_MaxGridIndexes[d])
				indexes[d] = m_MaxGridIndexes[d];
		}
		for (d=0; d<GetPointDimension(); d++)
		{
			indexes[GetPointDimension() + d] = (int)((tp.dir[d]+1.0) / m_dGridDirectionSize); 
			if (indexes[GetPointDimension() + d] >= m_MaxGridIndexes[GetPointDimension() + d])
				indexes[GetPointDimension() + d] = m_MaxGridIndexes[GetPointDimension() + d];
		}
	}

	inline int GetGridIndex(const mylablib::CMLDataVector<int>& indexes) const
	{
		int d;
		int nIndex = 0;
		for (d=0;d<indexes.size(); d++)
			nIndex += indexes[d] * m_vGridIndexFactors[d];
		return nIndex;
	}

	inline int GetGridIndex(const TangentPoint_& tp) const
	{
		mylablib::CMLDataVector<int> coordIndexes;
		coordIndexes.resize(GetPointDimension()*2);
		GetGridCoordIndexes(coordIndexes, tp);
		return GetGridIndex(coordIndexes);
	}

	inline int GetNumOfTrajs(void) const
	{
		return (int)m_pTrajs->Count();
	}

	inline int GetTotalPartitionCount() const
	{
		return m_nTotalPartitionCount;
	}

	inline int GetTotalTPCount() const
	{
		return m_nTotalTPCount;
	}


private:
	// Fields
	CMLTrajectoryCollection* m_pTrajs;
	mylablib::CMLDataPoint m_MaxLocationBoundary;
	mylablib::CMLDataPoint m_MinLocationBoundary;
	mylablib::CMLDataVector<int> m_vGridIndexFactors;
	mylablib::CMLDataVector<int> m_MaxGridIndexes;
	double m_dLocationDistWeight;
	double m_dDirectionDistWeight;
	double m_dOutlyingDist;
	double m_dOutlyingFrac;
	double m_dOutlyingPortion;
	double m_dGridLocationSize;
	double m_dGridDirectionSize;
	double m_dTangentSampleSize;
	int m_nPointDimension;
	int m_nTotalPartitionCount;
	int m_nTotalTPCount;
	std::vector<std::vector<double>> m_vTrajTPLengths;
	std::vector<double> m_vTrajLengths;
	std::vector<double> m_vTrajOutlyingLengths;
	std::vector<Grid_> m_vGrids;
	std::vector<int> m_vGridSearchRanges;
	long m_nMaxMemoryUsage;
};


#endif
